/*
 * SmartCam master processor library
 * Settings reading and writing.
 *
 *   Basic information:
 *     There are three basic components: targets, links and operations.
 *
 *     A component can be derived from a component of the same type.
 *     However, since links have no properties, deriving is only interesting
 *     in the case of targets and operations.
 *
 *     Component properties can be set if the proper environment is available.
 *     The relative nesting of those environments is unimportant, as long as
 *     it is uniquely determined.
 *
 *   It is possible to read different files one after the other; the information
 *   is then merged.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#include "globals.h"
#include "xmlparse.h"
#include "scutable.h"

#include "settings.h"

table_t scs_targets;           /* scs_target_t, owned */
table_t scs_operations;        /* scs_operation_t, owned */
table_t scs_target_operations; /* scs_target_operation_t, owned */
table_t scs_links;             /* scs_link_t, owned */
table_t scs_resources;         /* scs_resource_t, owned */
scs_mapper_t scs_mapper;

static int g_eltype_stack[32];

static XML_Parser g_parser = NULL;

static scs_resource_t *g_resource=NULL;
static scs_target_t *g_target=NULL;
static scs_target_resource_t *g_target_resource=NULL;
static scs_operation_t *g_operation=NULL;
static scs_operation_argument_t *g_operation_argument=NULL;
static scs_target_operation_t *g_target_operation=NULL;
static scs_target_operation_argument_t *g_target_operation_argument=NULL;
static scs_target_operation_resource_t *g_target_operation_resource=NULL;
static scs_link_t *g_link=NULL;
static scs_mapper_t *g_mapper=NULL;

static int g_line;
static int g_target_id = 0;
static int g_resource_id = 0;

static char g_argument[PATH_MAX] = {0};

static int amtoi(const char *nptr);
static double amtof(const char *nptr);

static void interpretProperty(const char *name, const char **atts);
static void interpretEnvironment(void *userData, const char *name, const char **atts);
static void startElement(void *userData, const char *name, const char **atts);
static void endElement(void *userData, const char *name);

static void fillTargetResource(void);
static void fillOperationArgument(void);
static void fillTargetOperation(void);
static void fillTargetOperationArgument(void);
static void fillTargetOperationResource(void);

static void
mergeTarget(scs_target_t *target, scs_target_t *base);
static void
mergeTargetResource(scs_target_resource_t *target_resource, scs_target_resource_t *base);
static void
mergeOperation(scs_operation_t *operation, scs_operation_t *base);
static void
mergeOperationArgument(scs_operation_argument_t *operation_argument, scs_operation_argument_t *base);
static void
mergeTargetOperation(scs_target_operation_t *target_operation, scs_target_operation_t *base);
static void
mergeTargetOperationArgument(scs_target_operation_argument_t *target_operation_argument,
				  scs_target_operation_argument_t *base);
static void
mergeTargetOperationResource(scs_target_operation_resource_t *target_operation_resource,
				  scs_target_operation_resource_t *base);

static void printTarget(FILE *file, scs_target_t *target);
static void printTargetResource(FILE *file, scs_target_resource_t *target_resource);
static void printOperation(FILE *file, scs_operation_t *operation);
static void printOperationArgument(FILE *file, scs_operation_argument_t *operation_argument);
static void printTargetOperation(FILE *file, scs_target_operation_t *target_operation);
static void
printTargetOperationArgument(FILE *file,
			     scs_target_operation_argument_t *target_operation_argument);
static void
printTargetOperationResource(FILE *file,
			     scs_target_operation_resource_t *target_operation_resource);
static void printLink(FILE *file, scs_link_t *link);

#define EXIT(x) { fprintf(stderr, " at line %d\n", g_line); exit(x); }
#define EXITINT(x) { fprintf(stderr, "internal error"); EXIT(x) }

#define EXITNEGATIVE(x) { if (x < 0) { fprintf(stderr, "internal error at line %d (%s, line %d)\n", g_line, __FILE__, __LINE__); exit(x); } }
#define EXITNULL(x) { if (x == 0){  fprintf(stderr, "internal error at line %d (%s, line %d)\n", g_line, __FILE__, __LINE__); exit(x); } }

#ifdef DEBUG
#define DBPRINTF(x) printf x
#else
#define DBPRINTF(x)
#endif

#define CHKENV1(x, xc) ((((unsigned int)xc)>0) == x)
#define CHKENV(t, r, tr, o, oa, to, toa, tor, l, m)(CHKENV1(t, g_target) && \
                                                    CHKENV1(r, g_resource) && \
                                                    CHKENV1(tr, g_target_resource) && \
                                                    CHKENV1(o, g_operation) && \
                                                    CHKENV1(oa, g_operation_argument) && \
                                                    CHKENV1(to, g_target_operation) && \
                                                    CHKENV1(toa, g_target_operation_argument) && \
                                                    CHKENV1(tor, g_target_operation_resource) && \
                                                    CHKENV1(m, g_mapper))
#define ERRENV(att) { fprintf(stderr,\
			      "Invalid environment for property '%s' {t%d r%d tr%d o%d oa%d to%d toa%d tor%d l%d m%d}", \
			      att,\
			      (g_target>0), \
                              (g_resource>0), \
			      (g_target_resource>0), \
			      (g_operation>0), \
			      (g_operation_argument>0), \
			      (g_target_operation>0), \
			      (g_target_operation_argument>0), \
			      (g_target_operation_resource>0), \
                              (g_link>0),\
                              (g_mapper>0)); \
                      EXIT(1); }

#define PRINTENV { fprintf(stderr,\
			      "Environment {t%p r%p tr%p o%p oa%p to%p toa%p tor%p l%p m%p}\n", \
			      (g_target), \
                              (g_resource), \
			      (g_target_resource), \
			      (g_operation), \
			      (g_operation_argument), \
			      (g_target_operation), \
			      (g_target_operation_argument), \
			      (g_target_operation_resource), \
                              (g_link), \
                              (g_mapper)); \
                      }



static int amtoi(const char *nptr)
{
  return amtof(nptr);
}

static double amtof(const char *nptr)
{
  double val = atof(nptr);
  
  if (strlen(nptr) > 1)
  {
    char m = nptr[strlen(nptr)-1];
  
    switch (m)
    {
      case 'a': return val * 0.000000000000000001;
      case 'f': return val * 0.000000000000001;
      case 'p': return val * 0.000000000001;
      case 'n': return val * 0.000000001;
      case 'u': return val * 0.000001;
      case 'm': return val * 0.001;
      case 'k': return val * 1000.0;
      case 'M': return val * 1000000.0;
      case 'G': return val * 1000000000.0;
      case 'T': return val * 1000000000000.0;
      case 'P': return val * 1000000000000000.0;
      case 'E': return val * 1000000000000000000.0;
    }
  }
  
  return val;
}

static void interpretProperty(const char *name, const char **atts)
{
  int i=-1;
  scs_target_t *target;
  scs_operation_t *operation;

  if (!atts[0])
  {
    fprintf(stderr, "Property without a name");
    EXIT(1);
  }

  if (!strcmp(atts[0], "derives"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target derives '%s'\n", atts[1]));

      // Find target
      while (tblgetnextelement(&scs_targets, &i, &target))
	if (!strcmp(target->name, atts[1]))
	{
	  g_target->derives = target;
	  break;
	}

      if (i < 0)
      {
	fprintf(stderr, "Couldn't find target for derivation '%s'", atts[1]);
	EXIT(1);
      }
    }
    else if (CHKENV(0, 0, 0, 1, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation derives '%s'\n", atts[1]));

      // Find operation
      while (tblgetnextelement(&scs_operations, &i, &operation))
	if (!strcmp(operation->name, atts[1]))
	{
	  g_operation->derives = operation;
	  break;
	}

      if (i < 0)
      {
	fprintf(stderr, "Couldn't find operation for derivation '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("derives");
  }
  else if (!strcmp(atts[0], "skeletondir"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target skeletondir to '%s'\n", atts[1]));

      g_target->skeletondir = (char*)malloc(strlen(atts[1])+1);
      strcpy(g_target->skeletondir, atts[1]);
    }
    else
      ERRENV("skeletondir");
  }
  else if (!strcmp(atts[0], "address"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target address to '%s'\n", atts[1]));

      g_target->address = (char*) malloc(strlen(atts[1])+1);
      strcpy(g_target->address, atts[1]);
    }
    else
      ERRENV("address");
  }
  else if (!strcmp(atts[0], "driver"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target driver to '%s'\n", atts[1]));

      g_target->driver = (char*)malloc(strlen(atts[1])+1);
      strcpy(g_target->driver, atts[1]);
    }
    else
      ERRENV("driver");
  }
  else if (!strcmp(atts[0], "pos"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target pos to '%s'\n", atts[1]));

      g_target->pos = (char*)malloc(strlen(atts[1])+1);
      strcpy(g_target->pos, atts[1]);
    }
    else
      ERRENV("pos");
  }
  else if (!strcmp(atts[0], "routing"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target routing to '%d'\n", amtoi(atts[1])));

      g_target->routing = amtoi(atts[1]);
    }
    else
      ERRENV("routing");
  }
  else if (!strcmp(atts[0], "power"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target power to '%f'\n", amtof(atts[1])));

      g_target->power = amtof(atts[1]);
    }
    else
      ERRENV("power");
  }
  else if (!strcmp(atts[0], "frequency"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target frequency to '%f'\n", amtof(atts[1])));

      g_target->frequency = amtof(atts[1]);
    }
    else
      ERRENV("frequency");
  }
  else if (!strcmp(atts[0], "parallel"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target parallel to '%d'\n", amtoi(atts[1])));

      g_target->parallel = amtoi(atts[1]);
    }
    else
      ERRENV("parallel");
  }
  else if (!strcmp(atts[0], "count"))
  {
    if (CHKENV(1, 1, 1, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target resource count to '%s'\n", atts[1]));

      g_target_resource->count = amtof(atts[1]);
    }
    else if (CHKENV(1, 1, 1, 1, 0, 1, 0, 1, 0, 0))
    {
      DBPRINTF(("target operation resource count to '%s'\n", atts[1]));

      g_target_operation_resource->count = amtof(atts[1]);
    }
    else
      ERRENV("count");
  }
  else if (!strcmp(atts[0], "shareable"))
  {
    if (CHKENV(1, 1, 1, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target resource shareable to '%s'\n", atts[1]));

      g_target_resource->shareable = amtoi(atts[1]);
    }
    else
      ERRENV("shareable");
  }
  else if (!strcmp(atts[0], "id"))
  {
/*  if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target id to '%s'\n", atts[1]));
    
      g_target->id = amtoi(atts[1]);
    }
    else
*/  if (CHKENV(0, 0, 0, 1, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation id to '%s'\n", atts[1]));

      g_operation->id = amtoi(atts[1]);
    }
    else if (CHKENV(0, 0, 0, 1, 1, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation argument id to '%s'\n", atts[1]));

      g_operation_argument->id = amtoi(atts[1]);
    }
    else
      ERRENV("id");
  }
  else if (!strcmp(atts[0], "skeleton"))
  {
    if (CHKENV(0, 0, 0, 1, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation skeleton to '%s'\n", atts[1]));

      g_operation->skeleton = (char*) malloc(strlen(atts[1])+1);
      strcpy(g_operation->skeleton, atts[1]);
    }
    else
      ERRENV("skeleton");
  }
  else if (!strcmp(atts[0], "kernel"))
  {
    if (CHKENV(0, 0, 0, 1, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation kernel to '%s'\n", atts[1]));
 
      g_operation->kernel = (char*) malloc(strlen(atts[1])+1);
      strcpy(g_operation->kernel, atts[1]);
    }
    else
      ERRENV("kernel");
  }
  else if (!strcmp(atts[0], "line"))
  {
    if (CHKENV(0, 0, 0, 1, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation line to '%s'\n", atts[1]));

      g_operation->line = amtoi(atts[1]);
    }
    else
      ERRENV("line");
  }
  else if (!strcmp(atts[0], "datatype"))
  {
    if (CHKENV(0, 0, 0, 1, 1, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation argument datatype to '%s'\n", atts[1]));

      g_operation_argument->datatype = (char*) malloc(strlen(atts[1])+1);
      strcpy(g_operation_argument->datatype, atts[1]);
    }
    else
      ERRENV("datatype");
  }
  else if (!strcmp(atts[0], "length"))
  {
    if (CHKENV(0, 0, 0, 1, 1, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation argument length to '%s'\n", atts[1]));

      g_operation_argument->length = amtoi(atts[1]);
    }
    else
      ERRENV("length");
  }
  else if (!strcmp(atts[0], "type"))
  {
    if (CHKENV(0, 0, 0, 1, 1, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation argument type to '%s'\n", atts[1]));

      if (!strcmp(atts[1], "stream"))
	g_operation_argument->type = SC_VAR_STREAM;
      else if (!strcmp(atts[1], "int"))
	g_operation_argument->type = SC_VAR_INTEGER;
      else if (!strcmp(atts[1], "double"))
	g_operation_argument->type = SC_VAR_DOUBLE;
      else if (!strcmp(atts[1], "pointer"))
	g_operation_argument->type = SC_VAR_POINTER;
      else
      {
	fprintf(stderr, "Unknown operation argument type '%s'", atts[1]);
	EXIT(1);
      }
    }
    else if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper type to '%s'\n", atts[1]));

      if (!strcmp(atts[1], "greedy"))
	g_mapper->type = SC_MAP_GREEDY;
      else if (!strcmp(atts[1], "combinatorial"))
	g_mapper->type = SC_MAP_COMBINATORIAL;
      else
      {
	fprintf(stderr, "Unknown mapper type '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("type");
  }
  else if (!strcmp(atts[0], "intent"))
  {
    if (CHKENV(0, 0, 0, 1, 1, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("operation argument intent to '%s'\n", atts[1]));

      if (!strcmp(atts[1], "in"))
	g_operation_argument->intent = SC_INTENT_IN;
      else if (!strcmp(atts[1], "out"))
	g_operation_argument->intent = SC_INTENT_OUT;
      else
      {
	fprintf(stderr, "Unknown operation argument intent '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("intent");
  }
  else if (!strcmp(atts[0], "affinity"))
  {
    if (CHKENV(1, 0, 0, 1, 0, 1, 0, 0, 0, 0))
    {
      DBPRINTF(("target operation affinity to '%s'\n", atts[1]));

      g_target_operation->affinity = amtof(atts[1]);
    }
    else
      ERRENV("affinity");
  }
  else if (!strcmp(atts[0], "time"))
  {
    if (CHKENV(1, 0, 0, 1, 0, 1, 0, 0, 0, 0))
    {
      DBPRINTF(("target operation time to '%s'\n", atts[1]));

      g_target_operation->time = amtof(atts[1]);
    }
    else
      ERRENV("time");
  }
  else if (!strcmp(atts[0], "energy"))
  {
    if (CHKENV(1, 0, 0, 1, 0, 1, 0, 0, 0, 0))
    {
      DBPRINTF(("target operation energy to '%s'\n", atts[1]));

      g_target_operation->energy = amtof(atts[1]);
    }
    else
      ERRENV("energy");
  }
  else if (!strcmp(atts[0], "delay"))
  {
    if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation delay to '%s'\n", atts[1]));

      g_target_operation_argument->delay = amtoi(atts[1]);
    }
    else if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper delay to '%s'\n", atts[1]));

      g_mapper->delay = amtoi(atts[1]);
    }
    else
      ERRENV("delay");
  }
  else if (!strcmp(atts[0], "elementsize"))
  {
    if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation argument elementsize to '%s'\n", atts[1]));

      g_target_operation_argument->elementsize = amtoi(atts[1]);
    }
    else
      ERRENV("elementsize");
  }
  else if (!strcmp(atts[0], "timedist"))
  {
    if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation argument timedist '%s'\n", atts[1]));

      if (!strcmp(atts[1], "regular"))
	g_target_operation_argument->timedist = SC_DIST_REGULAR;
      else if (!strcmp(atts[1], "bulk"))
	g_target_operation_argument->timedist = SC_DIST_BULK;
      else
      {
	fprintf(stderr, "Unknown target operation argument timedist '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("timedist");
  }
  else if (!strcmp(atts[0], "state"))
  {
    if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation argument state to '%s'\n", atts[1]));

      g_target_operation_argument->state = amtoi(atts[1]);
    }
    else
      ERRENV("state");
  }
  else if (!strcmp(atts[0], "buffer"))
  {
    if (CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 0, 0))
    {
      DBPRINTF(("target buffer to '%d'\n", amtoi(atts[1])));

      g_target->buffer = amtoi(atts[1]);
    }
    else if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation argument buffer to '%s'\n", atts[1]));

      g_target_operation_argument->buffer = amtoi(atts[1]);
    }
    else
      ERRENV("buffer");
  }
  else if (!strcmp(atts[0], "chunksize"))
  {
    if (CHKENV(1, 0, 0, 1, 1, 1, 1, 0, 0, 0))
    {
      DBPRINTF(("target operation argument chunksize to '%s'\n", atts[1]));

      g_target_operation_argument->chunksize = amtoi(atts[1]);
    }
    else
      ERRENV("");
  }
  else if (!strcmp(atts[0], "source"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 1, 0) || CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 1, 0))
    {
      int i=-1;
      scs_target_t *target;

      DBPRINTF(("link source to '%s'\n", atts[1]));

      // Find target
      while (tblgetnextelement(&scs_targets, &i, &target))
	if (!strcmp(target->name, atts[1]))
	{
	  g_link->source = target;
	  break;
	}

      if (i < 0)
      {
	fprintf(stderr, "Couldn't find link source '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("source");
  }
  else if (!strcmp(atts[0], "destination"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 1, 0) || CHKENV(1, 0, 0, 0, 0, 0, 0, 0, 1, 0))
    {
      int i=-1;
      scs_target_t *target;

      DBPRINTF(("link destination to '%s'\n", atts[1]));

      // Find target
      while (tblgetnextelement(&scs_targets, &i, &target))
	if (!strcmp(target->name, atts[1]))
	{
	  g_link->destination = target;
	  break;
	}

      if (i < 0)
      {
	fprintf(stderr, "Couldn't find link destination '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("destination");
  }
  else if (!strcmp(atts[0], "maxtime"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper maxtime to '%s'\n", atts[1]));

      g_mapper->maxtime = amtof(atts[1]);
    }
    else
      ERRENV("maxtime");
  }
  else if (!strcmp(atts[0], "avgtime"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper avgtime to '%s'\n", atts[1]));

      g_mapper->avgtime = amtof(atts[1]);
    }
    else
      ERRENV("avgtime");
  }
  else if (!strcmp(atts[0], "avgsqtime"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper avgsqtime to '%s'\n", atts[1]));

      g_mapper->avgsqtime = amtof(atts[1]);
    }
    else
      ERRENV("avgsqtime");
  }
  else if (!strcmp(atts[0], "avgenergy"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper avgenergy to '%s'\n", atts[1]));

      g_mapper->avgenergy = amtof(atts[1]);
    }
    else
      ERRENV("avgenergytime");
  }
  else if (!strcmp(atts[0], "avgresources"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper avgresources to '%s'\n", atts[1]));

      g_mapper->avgresources = amtof(atts[1]);
    }
    else
      ERRENV("avgresources");
  }
  else if (!strcmp(atts[0], "evaluator"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper evaluator to '%s'\n", atts[1]));

      if (!strcmp(atts[1], "flow"))
	g_mapper->evaluator = SC_EVL_FLOW;
      else if (!strcmp(atts[1], "unrelated"))
	g_mapper->evaluator = SC_EVL_UNRELATED;
      else if (!strcmp(atts[1], "dependent"))
	g_mapper->evaluator = SC_EVL_DEPENDENT;
      else
      {
	fprintf(stderr, "Unknown mapper evaluator '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("evaluator");
  }
  else if (!strcmp(atts[0], "graph"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper graph to '%s'\n", atts[1]));

      if (!strcmp(atts[1], "unconnected"))
	g_mapper->graph = SC_GRAPH_UNCONNECTED;
      else if (!strcmp(atts[1], "tig"))
	g_mapper->graph = SC_GRAPH_TIG;
      else if (!strcmp(atts[1], "tdg"))
	g_mapper->graph = SC_GRAPH_TDG;
      else if (!strcmp(atts[1], "dtig"))
	g_mapper->graph = SC_GRAPH_DTIG;
      else
      {
	fprintf(stderr, "Unknown mapper graph '%s'", atts[1]);
	EXIT(1);
      }
    }
    else
      ERRENV("graph");
  }
  else if (!strcmp(atts[0], "iterations"))
  {
    if (CHKENV(0, 0, 0, 0, 0, 0, 0, 0, 0, 1))
    {
      DBPRINTF(("mapper iterations to '%s'\n", atts[1]));

      g_mapper->iterations = amtoi(atts[1]);
    }
    else
      ERRENV("iterations");
  }
  else
  {
    fprintf(stderr, "Unknown property '%s'", atts[0]);
    EXIT(1);
  }  
}

static void fillTargetResource(void)
{
  int i=-1;
  scs_target_resource_t *target_resource;

  while (tblgetnextelement(&g_target->resources, &i, &target_resource))
    if (target_resource->resource == g_resource)
      break;
  
  if (i < 0)
  {
    EXITNEGATIVE(tbladdnew(&g_target->resources, &target_resource))
      
    target_resource->resource = g_resource;
    target_resource->target = g_target;
    target_resource->count = -1;
    target_resource->shareable = -1;
  }
  
  g_target_resource = target_resource;

  if (g_operation)
    fillTargetOperationResource();
}

static void fillOperationArgument(void)
{
  int i=-1;
  scs_operation_argument_t *operation_argument;

  while (tblgetnextelement(&g_operation->arguments, &i, &operation_argument))
    if (!strcmp(operation_argument->name, g_argument))
      break;
  
  if (i < 0)
  {
    EXITNEGATIVE(tbladdnew(&g_operation->arguments, &operation_argument));
    operation_argument->name = (char*) malloc(strlen(g_argument)+1);
    strcpy(operation_argument->name, g_argument);

    operation_argument->operation = g_operation;
    operation_argument->id = -1;
    operation_argument->type = -1;
    operation_argument->intent = -1;
  }
  
  g_operation_argument = operation_argument;

  if (g_target)
    fillTargetOperationArgument();
}

static void fillTargetOperation(void)
{
  int i=-1;
  scs_target_operation_t *target_operation;

  while (tblgetnextelement(&scs_target_operations, &i, &target_operation))
    if (target_operation->operation == g_operation &&
	target_operation->target == g_target)
      break;
  
  if (i < 0)
  {
    EXITNEGATIVE(tbladdnew(&scs_target_operations, &target_operation));
    target_operation->operation = g_operation;
    target_operation->target = g_target;
    
    EXITNEGATIVE(tbladd(&g_operation->targets, target_operation));
    EXITNEGATIVE(tbladd(&g_target->operations, target_operation));

    EXITNEGATIVE(tblcreate(&target_operation->arguments, sizeof(scs_target_operation_argument_t)));
    EXITNEGATIVE(tblcreate(&target_operation->resources, sizeof(scs_target_operation_resource_t)));

    target_operation->affinity = -1;
    target_operation->time = -1;
    target_operation->energy = -1;
  }
  
  g_target_operation = target_operation;
  
  if (g_argument[0])
    fillTargetOperationArgument();
  
  if (g_resource)
    fillTargetOperationResource();
}

static void fillTargetOperationArgument(void)
{
  int i=-1;
  scs_target_operation_argument_t *target_operation_argument;

  while (tblgetnextelement(&g_target_operation->arguments, &i, &target_operation_argument))
    if (target_operation_argument->operation_argument == g_operation_argument &&
	target_operation_argument->target_operation == g_target_operation)
      break;
  
  if (i < 0)
  {
    EXITNEGATIVE(tbladdnew(&g_target_operation->arguments, &target_operation_argument));
    target_operation_argument->operation_argument = g_operation_argument;
    target_operation_argument->target_operation = g_target_operation;

    target_operation_argument->delay = -1;
    target_operation_argument->state = -1;
    target_operation_argument->buffer = -1;
    target_operation_argument->elementsize = -1;
    target_operation_argument->chunksize = -1;
  }
  
  g_target_operation_argument = target_operation_argument;
}

static void fillTargetOperationResource(void)
{
  int i=-1;
  scs_target_operation_resource_t *target_operation_resource;

  while (tblgetnextelement(&g_target_operation->resources, &i, &target_operation_resource))
    if (target_operation_resource->target_resource == g_target_resource &&
	target_operation_resource->target_operation == g_target_operation)
      break;
  
  if (i < 0)
  {
    EXITNEGATIVE(tbladdnew(&g_target_operation->resources, &target_operation_resource));
    target_operation_resource->target_resource = g_target_resource;
    target_operation_resource->target_operation = g_target_operation;

    target_operation_resource->count = -1;
  }
  
  g_target_operation_resource = target_operation_resource;
}

#define ENVERR(env) { fprintf(stderr, "Cannot open '%s' environment twice", env); \
                      EXIT(1); }

static void interpretEnvironment(void *userData, const char *name, const char **atts)
{
  int *sp = (int*) userData;
  int i;
  const char *att, *val;
  char attclass[PATH_MAX];
  const char *emptyatt[1] = {NULL};

  for (i=0; atts[i]; i+= 2)
  {
    att = atts[i];
    val = atts[i+1];
    strcpy(attclass, att);
    strcat(attclass, "s");

    startElement((void*)sp, attclass, emptyatt);
    startElement((void*)sp, val, emptyatt);
  }
}

static void startElement(void *userData, const char *name, const char **atts)
{
  int *sp = (int*) userData;

  int i=-1;
  scs_target_t *target;
  scs_operation_t *operation;
  scs_resource_t *resource;
  scs_link_t *link;

  g_line = XML_GetCurrentLineNumber(g_parser);

  switch (g_eltype_stack[*sp])
  {
    /* *** COMPONENT CLASS ************************************************* */
    
    case ELTYPE_NONE:
    case ELTYPE_DOCUMENT:
    case ELTYPE_OPERATION:
    case ELTYPE_TARGET:
    case ELTYPE_ARGUMENT:
    case ELTYPE_RESOURCE:    
    case ELTYPE_LINK:
    case ELTYPE_ENVIRONMENT:
    case ELTYPE_MAPPER:
      if (!strcmp(name, "operations"))
      {
	if (g_operation) ENVERR("operation");
	g_eltype_stack[*sp+1] = ELTYPE_OPERATIONS;
      }
      else if (!strcmp(name, "targets"))
      {
	if (g_target) ENVERR("target");
	g_eltype_stack[*sp+1] = ELTYPE_TARGETS; 
      }
      else if (!strcmp(name, "arguments"))
      {
	if (g_argument[0]) ENVERR("argument");
	g_eltype_stack[*sp+1] = ELTYPE_ARGUMENTS; 
      }
      else if (!strcmp(name, "resources"))
      {
	if (g_resource) ENVERR("resource");
	g_eltype_stack[*sp+1] = ELTYPE_RESOURCES; 
      }
      else if (!strcmp(name, "links"))
      {
	if (g_link) ENVERR("link");
	g_eltype_stack[*sp+1] = ELTYPE_LINKS; 
      }
      else if (!strcmp(name, "document")/* && !*sp */)
	g_eltype_stack[*sp+1] = ELTYPE_DOCUMENT;
      else if (!strcmp(name, "env"))
      {
	g_eltype_stack[*sp+1] = ELTYPE_ENVIRONMENT;
	*sp += 1;
	interpretEnvironment(userData, name, atts);
	*sp -= 1;
      }
      else if (!strcmp(name, "mapper"))
      {
	if (g_mapper) ENVERR("mapper");
	g_mapper = &scs_mapper;
	g_eltype_stack[*sp+1] = ELTYPE_MAPPER;
      }
      else if (!strcmp(name, "E"))
      {
	g_eltype_stack[*sp+1] = ELTYPE_PROPERTY;
	interpretProperty(name, atts);
      }
      else
      {
	fprintf(stderr, "Expected: component class or property; got: '%s'", name);
	EXIT(1);
      }
      break;

    /* *** COMPONENT ******************************************************* */

    case ELTYPE_OPERATIONS:
      g_eltype_stack[*sp+1] = ELTYPE_OPERATION;

      DBPRINTF(("operation '%s'\n", name));

      // fill g_operation
      while (tblgetnextelement(&scs_operations, &i, &operation))
	if (!strcmp(operation->name, name))
	  break;

      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_operations, &operation));
	operation->name = (char*) malloc(strlen(name)+1);
	strcpy(operation->name, name);

	EXITNEGATIVE(tblcreate(&operation->arguments, sizeof(scs_operation_argument_t)));
	EXITNEGATIVE(tblcreate(&operation->targets, sizeof(scs_target_operation_t)));

	operation->id = -1;
	operation->line = -1;
      }

      g_operation = operation;

      if (g_argument[0])
	fillOperationArgument();

      if (g_target)
	fillTargetOperation();
      break;
    case ELTYPE_TARGETS:
      g_eltype_stack[*sp+1] = ELTYPE_TARGET;
      
      DBPRINTF(("target '%s'\n", name));

      // fill g_target
      while (tblgetnextelement(&scs_targets, &i, &target))
	if (!strcmp(target->name, name))
	  break;

      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_targets, &target));
	target->name = (char*) malloc(strlen(name)+1);
	strcpy(target->name, name);
	target->id = ++g_target_id;

	EXITNEGATIVE(tblcreate(&target->resources, sizeof(scs_target_resource_t)));
	EXITNEGATIVE(tblcreate(&target->operations, sizeof(scs_target_operation_t)));

	target->routing = -1;
	target->buffer = -1;
	target->power = -1;
	target->frequency = -1;
	target->parallel = -1;
      }

      g_target = target;

      if (g_resource)
	fillTargetResource();

      if (g_operation)
	fillTargetOperation();

      break;
     case ELTYPE_ARGUMENTS:
      g_eltype_stack[*sp+1] = ELTYPE_ARGUMENT;
      
      DBPRINTF(("argument '%s'\n", name));

      strcpy(g_argument, name);

      if (g_operation)
	fillOperationArgument();

      break;
    case ELTYPE_RESOURCES:    
      g_eltype_stack[*sp+1] = ELTYPE_RESOURCE;

      DBPRINTF(("resource '%s'\n", name));
      
      // fill g_resource
      while (tblgetnextelement(&scs_resources, &i, &resource))
	if (!strcmp(resource->name, name))
	  break;

      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_resources, &resource));
	resource->name = (char*) malloc(strlen(name)+1);
	strcpy(resource->name, name);
	resource->id = ++g_resource_id;
      }

      g_resource = resource;

      if (g_target)
	fillTargetResource();

      break;
    case ELTYPE_LINKS:    
      g_eltype_stack[*sp+1] = ELTYPE_LINK;

      DBPRINTF(("link '%s'\n", name));

      // fill g_link
      while (tblgetnextelement(&scs_links, &i, &link))
	if (!strcmp(link->name, name))
	  break;

      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_links, &link));
	link->name = (char*) malloc(strlen(name)+1);
	strcpy(link->name, name);
      }

      g_link = link;    
      break;
    case ELTYPE_PROPERTY:
      fprintf(stderr, "Cannot subclass property at '%s'", name);
      EXIT(1);
      
    default:
      fprintf(stderr, "Unhandled element type: %d at %s (depth %d)", g_eltype_stack[*sp],
	      name, *sp);
      EXIT(1);
  }

  *sp += 1;

  if (strcmp(name, "env"))
    switch (g_eltype_stack[*sp])
    {
      case ELTYPE_OPERATION:
      case ELTYPE_TARGET:
      case ELTYPE_ARGUMENT:
      case ELTYPE_RESOURCE:
      case ELTYPE_LINK:
	for (i=0; atts[i]; i+= 2)
	  interpretProperty("E", &atts[i]);
    }
}

static void endElement(void *userData, const char *name)
{
  int *sp = (int*) userData;

  if (name && !strcmp(name, "env"))
  {
    while(g_eltype_stack[*sp] != ELTYPE_ENVIRONMENT)
    {
      endElement((void*)sp, NULL);
      *sp -= 1;
    }
  }
  else   
  {
    switch (g_eltype_stack[*sp])
    {
      case ELTYPE_OPERATION:
	g_operation = NULL;
	g_operation_argument = NULL;
	g_target_operation = NULL;
	g_target_operation_argument = NULL;
	g_target_operation_resource = NULL;
	break;
      case ELTYPE_TARGET:
	g_target = NULL;
	g_target_resource = NULL;
	g_target_operation = NULL;
	g_target_operation_argument = NULL;
	g_target_operation_resource = NULL;
	break;
      case ELTYPE_ARGUMENT:
	g_argument[0] = '\0';
	g_operation_argument = NULL;
	g_target_operation_argument = NULL;
	break;
      case ELTYPE_RESOURCE:
	g_resource = NULL;
	g_target_resource = NULL;
	g_target_operation_resource = NULL;
	break;
      case ELTYPE_LINK:
	/* If target is known, fill in source or destination */
	if (g_target)
	{
	  if (!g_link->source)
	    g_link->source = g_target;
	  else if (!g_link->destination)
	    g_link->destination = g_target;
	  else
	  {
	    fprintf(stderr, "Link in target environment should specific only 'source' or 'destination', not both");
	    EXIT(1);
	  }
	}

	if (!strcmp(g_link->name, "auto"))
	{
	  if (!g_link->source || !g_link->destination)
	  {
	    fprintf(stderr, "Cannot automatically name incomplete component");
	    EXIT(1);
	  }

	  free(g_link->name);
	  g_link->name = malloc(strlen(g_link->source->name) + strlen(g_link->destination->name) + 5);
	  sprintf(g_link->name, "%s-to-%s", g_link->source->name, g_link->destination->name);
	}

	g_link = NULL;
	break;
      case ELTYPE_MAPPER:
	g_mapper = NULL;
    }
  }

  *sp -= 1;
}


/* Warning: after this function, some targets may have non-owned
 * resources and operations.
 * (that seems obsolete; now, copies are made and merged downwards)
 */
static void derive(void)
{
  int i=-1;
  scs_target_t *target;
  scs_operation_t *operation;

  while (tblgetnextelement(&scs_targets, &i, &target))
    if (target->derives)
      mergeTarget(target, target->derives);

  while (tblgetnextelement(&scs_operations, &i, &operation))
    if (operation->derives)
      mergeOperation(operation, operation->derives);
}

static void mergeTarget(scs_target_t *target, scs_target_t *base)
{
  if (!target->skeletondir && base->skeletondir)
  {
    target->skeletondir = (char*) malloc(strlen(base->skeletondir)+1);
    strcpy(target->skeletondir, base->skeletondir);
  }
  
  if (!target->address && base->address)
  {
    target->address = (char*) malloc(strlen(base->address)+1);
    strcpy(target->address, base->address);
  }

  if (!target->driver && base->driver)
  {
    target->driver = (char*) malloc(strlen(base->driver)+1);
    strcpy(target->driver, base->driver);
  }
  
  if (!target->pos && base->pos)
  {
    target->pos = (char*) malloc(strlen(base->pos)+1);
    strcpy(target->pos, base->pos);
  }
  
  if (target->routing == -1)
  {
    target->routing = base->routing;
  }

  if (target->buffer == -1)
  {
    target->buffer = base->buffer;
  }

  if (target->power == -1)
  {
    target->power = base->power;
  }

  if (target->frequency == -1)
  {
    target->frequency = base->frequency;
  }

  if (target->parallel == -1)
  {
    target->parallel = base->parallel;
  }

  {
    int i=-1;
    scs_target_resource_t *resource1;
    
    while (tblgetnextelement(&base->resources, &i, &resource1))
    {
      int i=-1;
      scs_target_resource_t *resource2;
      
      while (tblgetnextelement(&target->resources, &i, &resource2))
	if (resource1->resource == resource2->resource)
	  break;
      
      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&target->resources, &resource2));
	resource2->target = target;
	resource2->resource = resource1->resource;
	resource2->count = -1;
	resource2->shareable = -1;
      }

      mergeTargetResource(resource2, resource1);
    }
  }
  
  {
    int i=-1;
    scs_target_operation_t *operation1;
    
    while (tblgetnextelement(&base->operations, &i, &operation1))
    {
      int i=-1;
      scs_target_operation_t *operation2;
      
      while (tblgetnextelement(&target->operations, &i, &operation2))
	if (operation1->operation == operation2->operation)
	  break;
      
      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_target_operations, &operation2));
	EXITNEGATIVE(tbladd(&target->operations, operation2));
	EXITNEGATIVE(tbladd(&operation1->operation->targets, operation2));

	operation2->target = target;
	operation2->operation = operation1->operation;
	operation2->affinity = -1;
	operation2->time = -1;
	operation2->energy = -1;

	EXITNEGATIVE(tblcreate(&operation2->arguments, sizeof(scs_target_operation_argument_t)));
	EXITNEGATIVE(tblcreate(&operation2->resources, sizeof(scs_target_operation_resource_t)));
      }

      mergeTargetOperation(operation2, operation1);
    }
  }
}

static void mergeTargetResource(scs_target_resource_t *target_resource, scs_target_resource_t *base)
{
  if (target_resource->count == -1)
  {
    target_resource->count = base->count;
  }

  if (target_resource->shareable == -1)
  {
    target_resource->shareable = base->shareable;
  }
}

static void mergeOperation(scs_operation_t *operation, scs_operation_t *base)
{
  if (!operation->skeleton && base->skeleton)
  {
    operation->skeleton = (char*) malloc(strlen(base->skeleton)+1);
    strcpy(operation->skeleton, base->skeleton);
  }
  
  if (!operation->kernel && base->kernel)
  {
    operation->kernel = (char*) malloc(strlen(base->kernel)+1);
    strcpy(operation->kernel, base->kernel);
  }
  
  if (operation->line == -1)
  {
    operation->line = base->line;
  }

  {
    int i=-1;
    scs_operation_argument_t *argument1;
    
    while (tblgetnextelement(&base->arguments, &i, &argument1))
    {
      int i=-1;
      scs_operation_argument_t *argument2;
      
      while (tblgetnextelement(&operation->arguments, &i, &argument2))
	if (!strcmp(argument1->name, argument2->name))
	  break;
      
      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&operation->arguments, &argument2));
	argument2->operation = operation;
	argument2->id = -1;
	argument2->type = -1;
	argument2->intent = -1;
	argument2->length = -1;
      }

      mergeOperationArgument(argument2, argument1);
    }
  }
  
  {
    int i=-1;
    scs_target_operation_t *target1;
    
    while (tblgetnextelement(&base->targets, &i, &target1))
    {
      int i=-1;
      scs_target_operation_t *target2;
      
      while (tblgetnextelement(&operation->targets, &i, &target2))
	if (target1->target == target2->target)
	  break;
      
      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&scs_target_operations, &target2));
	EXITNEGATIVE(tbladd(&operation->targets, target2));
	EXITNEGATIVE(tbladd(&target1->target->operations, target2));

	target2->operation = operation;
	target2->target = target1->target;
	target2->affinity = -1;
	target2->time = -1;
	target2->energy = -1;

	EXITNEGATIVE(tblcreate(&target2->arguments, sizeof(scs_target_operation_argument_t)));
	EXITNEGATIVE(tblcreate(&target2->resources, sizeof(scs_target_operation_resource_t)));
      }

      mergeTargetOperation(target2, target1);
    }
  }
}

static void mergeOperationArgument(scs_operation_argument_t *operation_argument, scs_operation_argument_t *base)
{
  if (operation_argument->id == -1)
  {
    operation_argument->id = base->id;
  }

  if (!operation_argument->name && base->name)
  {
    operation_argument->name = (char*) malloc(strlen(base->name)+1);
    strcpy(operation_argument->name, base->name);
  }
  
  if (!operation_argument->datatype && base->datatype)
  {
    operation_argument->datatype = (char*) malloc(strlen(base->datatype)+1);
    strcpy(operation_argument->datatype, base->datatype);
  }
  
  if (operation_argument->type == -1)
  {
    operation_argument->type = base->type;
  }

  if (operation_argument->intent == -1)
  {
    operation_argument->intent = base->intent;
  }

  if (operation_argument->length == -1)
  {
    operation_argument->length = base->length;
  }
}

static void mergeTargetOperation(scs_target_operation_t *target_operation, scs_target_operation_t *base)
{
  if (target_operation->affinity == -1)
  {
    target_operation->affinity = base->affinity;
  }

  if (target_operation->time == -1)
  {
    target_operation->time = base->time;
  }

  if (target_operation->energy == -1)
  {
    target_operation->energy = base->energy;
  }

  {
    int i=-1;
    scs_target_operation_argument_t *argument1;
    
    while (tblgetnextelement(&base->arguments, &i, &argument1))
    {
      int i=-1;
      scs_target_operation_argument_t *argument2;
      
      while (tblgetnextelement(&target_operation->arguments, &i, &argument2))
	if (argument1->operation_argument == 
	    argument2->operation_argument)
	  break;
      
      if (i < 0)
      {
	EXITNEGATIVE(tbladdnew(&target_operation->arguments, &argument2));
	argument2->target_operation = target_operation;
	argument2->operation_argument = argument1->operation_argument;
	argument2->state = -1;
	argument2->buffer = -1;
	argument2->delay = -1;
	argument2->elementsize = -1;
	argument2->chunksize = -1;
      }

      mergeTargetOperationArgument(argument2, argument1);
    }
  }  

  {
    int i=-1;
    scs_target_operation_resource_t *resource1;
    
    while (tblgetnextelement(&base->resources, &i, &resource1))
    {
      int i=-1;
      scs_target_operation_resource_t *resource2;
      
      while (tblgetnextelement(&target_operation->resources, &i, &resource2))
	if (resource1->target_resource->resource ==
	    resource2->target_resource->resource)
	  break;
      
      if (i < 0)
      {
	int i=-1;
	scs_target_resource_t *target_resource;

	EXITNEGATIVE(tbladdnew(&target_operation->resources, &resource2));

	i=-1;
	while (tblgetnextelement(&target_operation->target->resources, &i,
				 &target_resource))
	  if (target_resource->resource == resource1->target_resource->resource)
	    break;

	if (i < 0)
	{
	  fprintf(stderr, "Cannot find resource while deriving target operation");
	  exit(1);
	}

	resource2->target_operation = target_operation;
	resource2->target_resource = target_resource;
	resource2->count = -1;
      }

      mergeTargetOperationResource(resource2, resource1);
    }
  }  
}

static void mergeTargetOperationArgument(scs_target_operation_argument_t *target_operation_argument,
				  scs_target_operation_argument_t *base)
{
  if (target_operation_argument->delay == -1)
  {
    target_operation_argument->delay = base->delay;
  }  

  if (target_operation_argument->state == -1)
  {
    target_operation_argument->state = base->state;
  }  

  if (target_operation_argument->buffer == -1)
  {
    target_operation_argument->buffer = base->buffer;
  }  

  if (target_operation_argument->elementsize == -1)
  {
    target_operation_argument->elementsize = base->elementsize;
  }  

  if (target_operation_argument->chunksize == -1)
  {
    target_operation_argument->chunksize = base->chunksize;
  }

  if (!target_operation_argument->timedist && base->timedist)
  {
    target_operation_argument->timedist = base->timedist;
  }

}

static void mergeTargetOperationResource(scs_target_operation_resource_t *target_operation_resource,
				  scs_target_operation_resource_t *base)
{
  if (target_operation_resource->count == -1)
  {
    target_operation_resource->count = base->count;
  }  
}

void scSetPrint(FILE *file)
{
  fprintf(file, "<document>\n");

  if (tblgetelements(&scs_operations))
  {
    fprintf(file, "  <operations>\n");
    {
      int i=-1;
      scs_operation_t *operation;
      
      while (tblgetnextelement(&scs_operations, &i, &operation))
	printOperation(file, operation);    
    }
    fprintf(file, "  </operations>\n");
  }

  if (tblgetelements(&scs_targets))
  {
    fprintf(file, "  <targets>\n");
    {
      int i=-1;
      scs_target_t *target;
      
      while (tblgetnextelement(&scs_targets, &i, &target))
	printTarget(file, target);    
    }
    fprintf(file, "  </targets>\n");
  }

  if (tblgetelements(&scs_links))
  {
    fprintf(file, "  <links>\n");
    {
      int i=-1;
      scs_link_t *link;
      
      while (tblgetnextelement(&scs_links, &i, &link))
	printLink(file, link);    
    }
    fprintf(file, "  </links>\n");
  }

  fprintf(file, "</document>\n");
}

static void printTarget(FILE *file, scs_target_t *target)
{
  fprintf(file, "    <%s> <!-- %p -->\n", target->name, target);
  if (target->skeletondir)
    fprintf(file, "      <E skeletondir = \"%s\"/>\n", target->skeletondir);
  if (target->address)
    fprintf(file, "      <E address = \"%s\"/>\n", target->address);
  if (target->driver)
    fprintf(file, "      <E driver = \"%s\"/>\n", target->driver);
  if (target->pos)
    fprintf(file, "      <E pos = \"%s\"/>\n", target->pos);
  if (target->routing != -1)
    fprintf(file, "      <E routing = \"%d\"/>\n", target->routing);
  if (target->buffer != -1)
    fprintf(file, "      <E buffer = \"%d\"/>\n", target->buffer);
  if (target->power != -1)
    fprintf(file, "      <E power = \"%f\"/>\n", target->power);
  if (target->frequency != -1)
    fprintf(file, "      <E frequency = \"%f\"/>\n", target->frequency);

  if (tblgetelements(&target->resources))
  {
    int i=-1;
    scs_target_resource_t *target_resource;
    
    fprintf(file, "      <resources>\n");

    while (tblgetnextelement(&target->resources, &i, &target_resource))
      printTargetResource(file, target_resource);    
    
    fprintf(file, "      </resources>\n");
  }
      
  if (tblgetelements(&target->operations))
  {
    int i=-1;
    scs_target_operation_t *target_operation;
	
    fprintf(file, "      <operations>\n");

    while (tblgetnextelement(&target->operations, &i, &target_operation))
      printTargetOperation(file, target_operation);    

    fprintf(file, "      </operations>\n");
  }

  fprintf(file, "    </%s>\n", target->name);
}

static void printTargetResource(FILE *file, scs_target_resource_t *target_resource)
{
  fprintf(file, "        <%s>\n", target_resource->resource->name);

  if (target_resource->count != -1)
    fprintf(file, "          <E count = \"%g\"/>\n", target_resource->count);
  if (target_resource->shareable != -1)
    fprintf(file, "          <E shareable = \"%d\"/>\n", target_resource->shareable);

  fprintf(file, "        </%s>\n", target_resource->resource->name);
}

static void printOperation(FILE *file, scs_operation_t *operation)
{
  fprintf(file, "    <%s> <!-- %p -->\n", operation->name, operation);

  if (operation->id != -1)
    fprintf(file, "      <E id = \"%d\"/>\n", operation->id);

  if (operation->skeleton)
    fprintf(file, "      <E skeleton = \"%s\"/>\n", operation->skeleton);
  if (operation->kernel)
    fprintf(file, "      <E kernel = \"%s\"/>\n", operation->kernel);

  if (operation->line != -1)
    fprintf(file, "      <E line = \"%d\"/>\n", operation->line);

  if (tblgetelements(&operation->arguments))
  {
    int i=-1;
    scs_operation_argument_t *operation_argument;

    fprintf(file, "      <arguments>\n");

    while (tblgetnextelement(&operation->arguments, &i, &operation_argument))
      printOperationArgument(file, operation_argument);    

    fprintf(file, "      </arguments>\n");
  }

  fprintf(file, "    </%s>\n", operation->name);
}

static void printOperationArgument(FILE *file, scs_operation_argument_t *operation_argument)
{
  char *type;
  char *intent;

  fprintf(file, "        <%s>\n", operation_argument->name);

  if (operation_argument->id != -1)
    fprintf(file, "          <E id = \"%d\"/>\n", operation_argument->id);

  if (operation_argument->datatype)
    fprintf(file, "          <E datatype = \"%s\"/>\n", operation_argument->datatype);

  if (operation_argument->length != -1)
    fprintf(file, "          <E length = \"%d\"/>\n", operation_argument->length);

  if (operation_argument->type != -1)
  {
    switch (operation_argument->type)
    {
      case SC_VAR_STREAM: type = "stream"; break;
      case SC_VAR_INTEGER: type = "int"; break;
      case SC_VAR_DOUBLE: type = "double"; break;
      case SC_VAR_POINTER: type = "pointer"; break;
      default:
	fprintf(stderr, "Unknown operation argument type %d\n", operation_argument->type);
	EXIT(1);
    }
    
    fprintf(file, "          <E type = \"%s\"/>\n", type);
  }

  if (operation_argument->intent != -1)
  {
    switch (operation_argument->intent)
    {
      case SC_INTENT_IN: intent = "in"; break;
      case SC_INTENT_OUT: intent = "out"; break;
      default:
	fprintf(stderr, "Unknown operation argument intent %d\n", operation_argument->intent);
	EXIT(1);
    }

    fprintf(file, "          <E intent = \"%s\"/>\n", intent);
  }
  fprintf(file, "        </%s>\n", operation_argument->name);
}

static void printTargetOperation(FILE *file, scs_target_operation_t *target_operation)
{
  fprintf(file, "        <%s> <!-- %p.%p -->\n", target_operation->operation->name,
    target_operation, target_operation->operation);

  if (target_operation->affinity != -1)
    fprintf(file, "          <E affinity = \"%g\"/>\n", target_operation->affinity);

  if (target_operation->time != -1)
    fprintf(file, "          <E time = \"%g\"/>\n", target_operation->time);

  if (target_operation->energy != -1)
    fprintf(file, "          <E energy = \"%g\"/>\n", target_operation->energy);

  if (tblgetelements(&target_operation->arguments))
  {
    int i=-1;
    scs_target_operation_argument_t *target_operation_argument;

    fprintf(file, "          <arguments>\n");

    while (tblgetnextelement(&target_operation->arguments, &i, &target_operation_argument))
      printTargetOperationArgument(file, target_operation_argument);    

    fprintf(file, "          </arguments>\n");
  }

  if (tblgetelements(&target_operation->resources))
  {
    int i=-1;
    scs_target_operation_resource_t *target_operation_resource;

    fprintf(file, "          <resources>\n");

    while (tblgetnextelement(&target_operation->resources, &i, &target_operation_resource))
      printTargetOperationResource(file, target_operation_resource);    

    fprintf(file, "          </resources>\n");
  }

  fprintf(file, "        </%s>\n", target_operation->operation->name);
}

static void printTargetOperationArgument(FILE *file, scs_target_operation_argument_t *target_operation_argument)
{
  char *timedist;

  fprintf(file, "            <%s> <!-- %p.%p.%p -->\n", target_operation_argument->operation_argument->name, target_operation_argument->target_operation->target, target_operation_argument->target_operation->operation, target_operation_argument);

  if (target_operation_argument->delay != -1)
    fprintf(file, "              <E delay = \"%d\"/>\n", target_operation_argument->delay);

  if (target_operation_argument->state != -1)
    fprintf(file, "              <E state = \"%d\"/>\n", target_operation_argument->state);

  if (target_operation_argument->buffer != -1)
    fprintf(file, "              <E buffer = \"%d\"/>\n", target_operation_argument->buffer);

  if (target_operation_argument->chunksize != -1)
    fprintf(file, "              <E chunksize = \"%d\"/>\n", target_operation_argument->chunksize);

  if (target_operation_argument->elementsize != -1)
    fprintf(file, "              <E elementsize = \"%d\"/>\n", target_operation_argument->elementsize);

  if (target_operation_argument->timedist != -1)
  {
    switch (target_operation_argument->timedist)
    {
      case SC_DIST_REGULAR: timedist = "regular"; break;
      case SC_DIST_BULK:    timedist = "bulk"; break;
      default:
	fprintf(stderr, "Unknown target operation argument timedist %d\n", target_operation_argument->timedist);
	EXIT(1);    
    }

    fprintf(file, "              <E timedist = \"%s\"/>\n", timedist);
  }

  fprintf(file, "            </%s>\n", target_operation_argument->operation_argument->name);
}

static void printTargetOperationResource(FILE *file, scs_target_operation_resource_t *target_operation_resource)
{
  fprintf(file, "            <%s>\n", target_operation_resource->target_resource->resource->name);

  if (target_operation_resource->count != -1)
    fprintf(file, "              <E count = \"%g\"/>\n", target_operation_resource->count);

  fprintf(file, "            </%s>\n", target_operation_resource->target_resource->resource->name);
}

static void printLink(FILE *file, scs_link_t *link)
{
  fprintf(file, "    <%s>\n", link->name);

  if (link->source)
    fprintf(file, "      <E source = \"%s\"/>\n", link->source->name);

  if (link->destination)
    fprintf(file, "      <E destination = \"%s\"/>\n", link->destination->name);

  fprintf(file, "    </%s>\n", link->name);
}

/* *** printMapper *** */

void scSetInit(void)
{
  // Initialize tables
  EXITNEGATIVE(tblcreate(&scs_targets, sizeof(scs_target_t)));
  EXITNEGATIVE(tblcreate(&scs_operations, sizeof(scs_operation_t)));
  EXITNEGATIVE(tblcreate(&scs_target_operations, sizeof(scs_target_operation_t)));
  EXITNEGATIVE(tblcreate(&scs_links, sizeof(scs_link_t)));
  EXITNEGATIVE(tblcreate(&scs_resources, sizeof(scs_resource_t)));

  memset(&scs_mapper, 0, sizeof(scs_mapper_t));
  scs_mapper.maxtime = 1; /* Default: minimum makespan mapper */
  scs_mapper.graph = SC_GRAPH_DTIG;
  g_target_id = 0;
  g_resource_id = 0;
  g_argument[0] = '\0';
}

void scSetRead(char *name)
{
  FILE *file;
  char buf[BUFSIZ];
  int done;
  int sp=0;

  // Set up parser
  g_parser = XML_ParserCreate(NULL);
  XML_SetUserData(g_parser, &sp);
  XML_SetElementHandler(g_parser, startElement, endElement);

  if (!(file = fopen(name, "r")))
  {
    fprintf(stderr, "Couldn't open '%s'\n", name);
    exit(1);
  }

  // Parse
  do {
    size_t len = fread(buf, 1, sizeof(buf), file);
    done = len < sizeof(buf);
    if (!XML_Parse(g_parser, buf, len, done)) {
      fprintf(stderr,
              "%s at line %d\n",
              XML_ErrorString(XML_GetErrorCode(g_parser)),
              XML_GetCurrentLineNumber(g_parser));
      exit(1);
    }
  } while (!done);
  XML_ParserFree(g_parser);

  fclose(file);

  // Derive
  derive();
}

void scSetWrite(char *name)
{
  FILE *file;
  
  if (!(file = fopen(name, "w")))
  {
    fprintf(stderr, "Couldn't open '%s'\n", name);
    exit(1);
  }

  scSetPrint(file);

  fclose(file);
}

void scSetIndex(void)
{
  /* When read, operations, targets, arguments, etc., are not entered into the
   * tables according to their indices. This makes locating them quite expensive.
   * This routine set the indices to their correct values */

  table_t operations, targets, resources;
  scs_operation_t *o;
  scs_target_t *t;
  scs_resource_t *r;
  int id=-1;

  EXITNEGATIVE(tblcreate(&operations, sizeof(scs_operation_t)));

  /* Operations */
  while (tblgetnextelement(&scs_operations, &id, &o))
  {
    table_t arguments, targets;
    scs_operation_argument_t *a;
    scs_target_operation_t *t;
    int id=-1;

    if (o->id == -1)
    {
      fprintf(stderr, "Operation '%s' does not have an id\n", o->name);
      exit(1);
    }

    EXITNEGATIVE(tbladdi(&operations, o, o->id));

    /* Operation arguments */
    EXITNEGATIVE(tblcreate(&arguments, sizeof(scs_operation_argument_t)));

    while (tblgetnextelement(&o->arguments, &id, &a))
    {
      if (a->id == -1)
      {
	fprintf(stderr, "Argument '%s' of operation '%s' does not have an id\n",
		a->name, o->name);
	exit(1);
      }

      EXITNEGATIVE(tbladdi(&arguments, a, a->id));
    }

    EXITNEGATIVE(tblsortslice(&arguments, SCU_TABLE_SLICE_OCCUPIED));
    EXITNEGATIVE(tbldestroy(&o->arguments, 0));
    o->arguments = arguments;

    /* Target operations */
    EXITNEGATIVE(tblcreate(&targets, sizeof(scs_target_operation_t)));

    while (tblgetnextelement(&o->targets, &id, &t))
    {
      if (t->target->id == -1)
      {
	fprintf(stderr, "Target '%s' of operation '%s' does not have an id\n",
		t->target->name, o->name);
	exit(1);
      }

      tbladdi(&targets, t, t->target->id);
    }

    EXITNEGATIVE(tblsortslice(&targets, SCU_TABLE_SLICE_OCCUPIED));
    EXITNEGATIVE(tbldestroy(&o->targets, 0));
    o->targets = targets;
  }

  EXITNEGATIVE(tblsortslice(&operations, SCU_TABLE_SLICE_OCCUPIED));
  EXITNEGATIVE(tbldestroy(&scs_operations, 0));
  scs_operations = operations;

  /* Targets */
  EXITNEGATIVE(tblcreate(&targets, sizeof(scs_target_t)));

  while (tblgetnextelement(&scs_targets, &id, &t))
  {
    table_t resources, operations;
    scs_target_resource_t *r;
    scs_target_operation_t *o;
    int id=-1;

    if (t->id == -1)
    {
      fprintf(stderr, "Target '%s' does not have an id\n", t->name);
      exit(1);
    }

    tbladdi(&targets, t, t->id);

    /* Target resources */
    EXITNEGATIVE(tblcreate(&resources, sizeof(scs_target_resource_t)));

    while (tblgetnextelement(&t->resources, &id, &r))
    {
      if (r->resource->id == -1)
      {
	fprintf(stderr, "Resource '%s' of target '%s' does not have an id\n",
		r->resource->name, t->name);
	exit(1);
      }
      
      tbladdi(&resources, r, r->resource->id);
    }
    
    EXITNEGATIVE(tblsortslice(&resources, SCU_TABLE_SLICE_OCCUPIED));
    EXITNEGATIVE(tbldestroy(&t->resources, 0));
    t->resources = resources;

    /* Target operations */
    EXITNEGATIVE(tblcreate(&operations, sizeof(scs_target_operation_t)));

    while (tblgetnextelement(&t->operations, &id, &o))
    {
      table_t resources, arguments;
      scs_target_operation_resource_t *r;
      scs_target_operation_argument_t *a;
      int id=-1;

      if (o->operation->id == -1)
      {
	fprintf(stderr, "Operation '%s' of target '%s' does not have an id\n",
		o->operation->name, t->name);
	exit(1);
      }

      EXITNEGATIVE(tbladdi(&operations, o, o->operation->id));

      /* Target operation resources */
      EXITNEGATIVE(tblcreate(&resources, sizeof(scs_target_operation_resource_t)));

      while (tblgetnextelement(&o->resources, &id, &r))
      {
	if (r->target_resource->resource->id == -1)
	{
	  fprintf(stderr, "Resource '%s' of target operation '%s.%s' does not have an id\n",
		  r->target_resource->resource->name, t->name, o->operation->name);
	  exit(1);
	}

	tbladdi(&resources, r, r->target_resource->resource->id);
      }

      EXITNEGATIVE(tblsortslice(&resources, SCU_TABLE_SLICE_OCCUPIED));
      EXITNEGATIVE(tbldestroy(&o->resources, 0));
      o->resources = resources;

      /* Target operation arguments */
      EXITNEGATIVE(tblcreate(&arguments, sizeof(scs_target_operation_argument_t)));

      while (tblgetnextelement(&o->arguments, &id, &a))
      {
	if (a->operation_argument->id == -1)
	{
	  fprintf(stderr, "Argument '%s' of target operation '%s.%s' does not have an id\n",
		  a->operation_argument->name, t->name, o->operation->name);
	  exit(1);
	}

	EXITNEGATIVE(tbladdi(&arguments, a, a->operation_argument->id));
      }

      EXITNEGATIVE(tblsortslice(&arguments, SCU_TABLE_SLICE_OCCUPIED));
      EXITNEGATIVE(tbldestroy(&o->arguments, 0));
      o->arguments = arguments;
    }

    EXITNEGATIVE(tblsortslice(&operations, SCU_TABLE_SLICE_OCCUPIED));
    EXITNEGATIVE(tbldestroy(&t->operations, 0));
    t->operations = operations;
  }

  EXITNEGATIVE(tblsortslice(&targets, SCU_TABLE_SLICE_OCCUPIED));
  EXITNEGATIVE(tbldestroy(&scs_targets, 0));
  scs_targets = targets;

  /* Resources */
  EXITNEGATIVE(tblcreate(&resources, sizeof(scs_resource_t)));
  
  while (tblgetnextelement(&scs_resources, &id, &r))
  {
    if (r->id == -1)
    {
      fprintf(stderr, "Resource '%s' does not have an id\n", r->name);
      exit(1);
    }

    tbladdi(&resources, r, r->id);
  }

  EXITNEGATIVE(tblsortslice(&resources, SCU_TABLE_SLICE_OCCUPIED));
  EXITNEGATIVE(tbldestroy(&scs_resources, 0));
  scs_resources = resources;
}
